﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using XXF.BaseService.ConfigManager.Dal;
using XXF.BaseService.ConfigManager.Model;
using XXF.ProjectTool;
using XXF.Extensions;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using Newtonsoft.Json;

namespace XXF.BaseService.ConfigManager.SystemRuntime
{
    /// <summary>
    /// 配置心跳守护
    /// </summary>
    public class ConfigHeartbeatProtect : IDisposable
    {
        private CancellationTokenSource cancelSource;
        private static ConfigHeartbeatProtect configHeartbeatProtect = null;//单例实例
        private static object _instancelock = new object();//单例实例锁
        private static object _contextupdatelock = new object();//上下文更新锁 

        private RedisNetCommandListener redislistener = null;

        private ConfigHeartbeatProtect()
        {
            try
            {
                cancelSource = new CancellationTokenSource();

                System.Threading.Tasks.Task.Factory.StartNew(() =>
                {
                    HeatbeatRun();//注册心跳
                }, cancelSource.Token);


                redislistener = new RedisNetCommandListener(AppDomainContext.Context.ConfigParams.RedisServer); redislistener.Name = "统一配置中心";
                redislistener.Register((channel, msg) =>
                {
                    RedisListenerCommand(channel, msg);
                }, cancelSource, SystemParamConfig.Redis_Channel);
                LogHelper.Debug("ConfigHeartbeatProtect初始化成功");

            }
            catch (Exception exp)
            {
                LogHelper.Error(-1, "配置心跳初始化错误", exp);
                throw exp;
            }

        }
        /// <summary>
        /// 单例实例
        /// </summary>
        /// <returns></returns>
        public static ConfigHeartbeatProtect Instance()
        {
            if (configHeartbeatProtect != null)
                return configHeartbeatProtect;
            lock (_instancelock)
            {
                configHeartbeatProtect = new ConfigHeartbeatProtect();
                return configHeartbeatProtect;
            }
        }

        private void HeatbeatRun()
        {
            while (!cancelSource.IsCancellationRequested)
            {
                System.Threading.Thread.Sleep(SystemParamConfig.Config_HeatBeat_Every_Time * 1000);
                try
                {
                    LoadConfig(true);
                }
                catch (Exception exp)
                {
                    LogHelper.Error(-1, "配置心跳循环错误", exp);
                }
                LogHelper.Debug("配置心跳循环一次");
            }
        }

        private void RedisListenerCommand(string channel, string msg)
        {
            try
            {
                var command = new XXF.Serialization.JsonHelper().Deserialize<ConfigNetCommand>(msg);
                var categoryids = CommonHelper.GetProjectCategoryIDs(AppDomainContext.Context.ProjectModel.categoryids);
                if (categoryids.Contains(command.CategoryID) || AppDomainContext.Context.ProjectModel.projectname == command.ProjectName)
                {
                    if (command.CommandType == EnumCommandType.ConfigReload)
                        LoadConfig(false);
                    else
                        LoadConfig(true);
                }
                LogHelper.Log(-1, "执行命令,msg:" + msg.NullToEmpty());
                LogHelper.Debug("RedisListenerCommand命令执行一次,msg:" + msg);
            }
            catch (Exception exp)
            {
                LogHelper.Error(-1, "注册监听错误,msg:" + msg.NullToEmpty(), exp);
            }
        }

        public void LoadConfig(bool isupdateonly)
        {
            lock (_contextupdatelock)
            {
                string localcachexmlpath = AppDomain.CurrentDomain.BaseDirectory.Trim('\\') + "\\" + "temp\\" + "config.localcache.json";
                bool islocalcache = true;//是否需要序列化到本地缓存
                try
                {
                    DateTime? updatetime = null;
                    SqlHelper.ExcuteSql(AppDomainContext.Context.ConfigParams.ConfigManagerConnectString, false, (c) =>
                    {
                        updatetime = c.GetServerDate();
                        if (isupdateonly == false)
                        {
                            //加载系统配置
                            AppDomainContext.Context.ConfigParams.RedisServer = SystemConfigHelper.GetRedisServer(c);
                            redislistener.RedisServerIp = AppDomainContext.Context.ConfigParams.RedisServer;

                            //加载项目信息
                            Dal.tb_project_dal projectdal = new Dal.tb_project_dal();
                            var projectmodel = projectdal.Get(c, AppDomainContext.Context.ConfigParams.ProjectName);

                            //更新项目心跳
                            projectdal.UpdateLastheartbeattime(c, projectmodel.id);

                            //加载分类信息
                            Dal.tb_category_dal categorydal = new Dal.tb_category_dal();
                            var categoryids = CommonHelper.GetProjectCategoryIDs(projectmodel.categoryids);
                            var categorymodels = categorydal.GetListByCategoryIDs(c, categoryids);
                            categorymodels = CommonHelper.OrderByCategoryIDs(categorymodels, categoryids);//排序分类

                            //更新配置信息
                            Dal.tb_config_dal configdal = new Dal.tb_config_dal();
                            var configs = configdal.GetListByCategoryIDs(c, CommonHelper.GetCategoryIDs(categorymodels), DateTime.Parse("1900-01-01"));
                            var configinfos = ToConfigInfoModels(configs);
                            configinfos = CommonHelper.OrderByCategoryIDs(configinfos, categoryids);//根据分类顺序，排序配置进行插入
                            ConfigInfoOfKeyDic dic = new ConfigInfoOfKeyDic();
                            foreach (var config in configinfos)
                            {
                                dic.AddConfig(config);
                            }
                            AppDomainContext.Context.ProjectModel = projectmodel;
                            AppDomainContext.Context.CategoryModels = categorymodels;
                            AppDomainContext.Context.ConfigInfoOfKeyDic = dic;
                            LogHelper.Debug("重新加载配置成功");
                            LogHelper.Log(-1, "重新加载配置成功");
                        }
                        else
                        {
                            //刷新系统配置
                            redislistener.RedisServerIp = AppDomainContext.Context.ConfigParams.RedisServer;

                            //更新项目心跳
                            Dal.tb_project_dal projectdal = new Dal.tb_project_dal();
                            projectdal.UpdateLastheartbeattime(c, AppDomainContext.Context.ProjectModel.id);
                            //更新配置信息
                            Dal.tb_config_dal configdal = new Dal.tb_config_dal();
                            var updatedconfigs = configdal.GetListByCategoryIDs(c, CommonHelper.GetCategoryIDs(AppDomainContext.Context.CategoryModels), AppDomainContext.Context.LastUpdateTime);
                            var configinfos = ToConfigInfoModels(updatedconfigs);
                            var categoryids = CommonHelper.GetProjectCategoryIDs(AppDomainContext.Context.ProjectModel.categoryids);
                            foreach (var config in configinfos)
                            {
                                AppDomainContext.Context.ConfigInfoOfKeyDic.SetConfig(config, categoryids);
                            }
                            if (configinfos.Count == 0)
                                islocalcache = false;//本次没有更新配置
                            LogHelper.Debug("更新当前配置成功");
                        }
                    });
                    if (islocalcache)
                    {
                        //本地磁盘缓存
                        Common.IOHelper.CreateDirectory(localcachexmlpath);
                        var xml = JsonConvert.SerializeObject(AppDomainContext.Context); //use json.net,未来加密处理
                        System.IO.File.WriteAllText(localcachexmlpath, xml);
                        LogHelper.Debug("当前上下文序列化到磁盘成功");
                    }
                    if (updatetime != null)
                        AppDomainContext.Context.LastUpdateTime = updatetime.Value;

                }
                catch (Exception exp)
                {
                    LogHelper.Error(-1, "统一配置中心获取配置失败,本地磁盘缓存恢复上一次可用配置", exp);
                    //本地磁盘缓存恢复上一次可用配置
                    if (System.IO.File.Exists(localcachexmlpath))
                    {
                        var xml = System.IO.File.ReadAllText(localcachexmlpath);
                        AppDomainContext.Context = JsonConvert.DeserializeObject<ConfigContext>(xml);//use json.net,未来解密处理
                    }

                    LogHelper.Debug("统一配置中心获取配置失败,本地磁盘缓存恢复上一次可用配置");
                    //throw exp;
                }
            }

        }

        private List<ConfigInfoModel> ToConfigInfoModels(List<tb_config_model> models)
        {

            List<int> loadbalanceconfigids = new List<int>();
            List<int> failoverconfigids = new List<int>();
            foreach (var m in models)
            {
                if (m.loadbalancealgorithmenum >= 0)
                {
                    loadbalanceconfigids.Add(m.id);
                }
                if (m.failoversequence >= 0)
                {
                    failoverconfigids.Add(m.id);
                }
            }
            List<tb_loadbalance_model> loadbalancemodels = new List<tb_loadbalance_model>();
            Dictionary<int, Dictionary<string, tb_loadbalance_model>> loadbalancemodelsdic = new Dictionary<int, Dictionary<string, tb_loadbalance_model>>();
            List<tb_failover_model> failovermodels = new List<tb_failover_model>();
            Dictionary<int, Dictionary<string, tb_failover_model>> failovermodelsdic = new Dictionary<int, Dictionary<string, tb_failover_model>>();
            SqlHelper.ExcuteSql(AppDomainContext.Context.ConfigParams.ConfigManagerConnectString, false, (c) =>
            {
                loadbalancemodels = new tb_loadbalance_dal().GetListByCategoryIDs(c, loadbalanceconfigids);
                failovermodels = new tb_failover_dal().GetListByCategoryIDs(c, failoverconfigids);
            });

            foreach (var m in loadbalancemodels)
            {
                if (!loadbalancemodelsdic.ContainsKey(m.configid))
                {
                    loadbalancemodelsdic.Add(m.configid, new Dictionary<string, tb_loadbalance_model>());
                }
                loadbalancemodelsdic[m.configid].Add(m.sequence.ToString(), m);
            }

            foreach (var m in failovermodels)
            {
                if (!failovermodelsdic.ContainsKey(m.configid))
                {
                    failovermodelsdic.Add(m.configid, new Dictionary<string, tb_failover_model>());
                }
                failovermodelsdic[m.configid].Add(m.sequence.ToString(), m);
            }

            List<ConfigInfoModel> rs = new List<ConfigInfoModel>();
            foreach (var m in models)
            {
                var r = new ConfigInfoModel();
                r.configmodel = m;
                if (loadbalancemodelsdic.ContainsKey(m.id))
                    r.loadbalacemodels = loadbalancemodelsdic[m.id];
                if (failovermodelsdic.ContainsKey(m.id))
                    r.failovermodels = failovermodelsdic[m.id];
                rs.Add(r);
            }
            return rs;
        }

        public void Dispose()
        {
            if (cancelSource != null)
                cancelSource.Cancel();
            if (redislistener != null)
                redislistener.Dispose();
        }
    }
}
